﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace JsStubGenerator
{
    public class NamespaceFragment
    {
        public NamespaceFragment()
        {
            Types = new List<Type>();
            SubFragments = new Dictionary<string, NamespaceFragment>();
        }

        public string Name { get; set; }

        public List<Type> Types { get; set; }

        public Dictionary<string, NamespaceFragment> SubFragments { get; set; }

        public void Register(string relativeNamespace, Type type)
        {
            GetFragment(relativeNamespace, true).Types.Add(type);
            if (type.BaseType != typeof(object) && !type.IsEnum)
            {
                if (!HasType(type.BaseType))
                {
                    Register(relativeNamespace, type.BaseType);
                }
            }
        }

        public NamespaceFragment GetFragment(string relativeNamespace, bool createIfNotExisting)
        {
            if (relativeNamespace == string.Empty)
            {
                return this;
            }
            int indexOfDot = relativeNamespace.IndexOf('.');
            string subRelativeNamespace = relativeNamespace.Substring(0, indexOfDot > 0 ? indexOfDot : relativeNamespace.Length);
            NamespaceFragment subFragment;
            if (SubFragments.ContainsKey(subRelativeNamespace))
                subFragment = SubFragments[subRelativeNamespace];
            else if (createIfNotExisting)
            {
                subFragment = new NamespaceFragment();
                subFragment.Name = subRelativeNamespace;
                SubFragments[subRelativeNamespace] = subFragment;
            }
            else
                return null;
            return subFragment.GetFragment(indexOfDot > 0 ? relativeNamespace.Substring(subRelativeNamespace.Length + 1) : string.Empty, createIfNotExisting);
        }

        public bool HasType(Type type)
        {
            var ns = GetFragment(type.Namespace, false);
            if (ns == null)
                return false;
            return ns.Types.Contains(type);
        }

        public void Render(System.IO.TextWriter writer)
        {
            Render(writer, true);
        }

        public void Render(System.IO.TextWriter writer, bool isRoot)
        {
            bool isFirst = true;
            foreach (NamespaceFragment subFragment in SubFragments.Values)
            {
                if (!isRoot)
                {
                    if (isFirst)
                    {
                        isFirst = false;
                        writer.Write("\"");
                    }
                    else
                        writer.Write(", \"");

                    writer.Write(subFragment.Name);
                    writer.Write("\":");
                }
                else
                {
                    writer.Write(subFragment.Name);
                    writer.Write("=");
                }
                writer.Write("{ ");
                subFragment.Render(writer, false);
                writer.Write(" }");
            }
            foreach (Type type in Types)
            {
                if (isFirst)
                {
                    isFirst = false;
                    writer.Write("\"");
                }
                else
                    writer.Write(", \"");
                writer.Write(type.Name);
                writer.Write("\":");
                if (type.IsEnum)
                    RenderEnum(type, writer);
                else
                    writer.Write("function(){ }");
            }
        }

        public void RenderEnum(Type enumType, TextWriter writer)
        {
            writer.Write("{");
            bool isFirst = true;
            foreach (string enumName in Enum.GetNames(enumType))
            {
                if (isFirst)
                {
                    isFirst = false;
                    writer.Write("\"");
                }
                else
                    writer.Write(", \"");
                writer.Write(enumName);
                writer.Write("\":");
                writer.Write((int)Enum.Parse(enumType, enumName));
            }
            writer.Write("}");
        }
    }
}
